package cn.devzyh.oms.coms.flow.executor;

import cn.devzyh.oms.common.core.message.FlowErrorCodes;
import cn.devzyh.oms.common.core.util.R;
import cn.devzyh.oms.coms.api.flow.entity.Edge;
import cn.devzyh.oms.coms.api.flow.entity.Flow;
import cn.devzyh.oms.coms.api.flow.entity.Node;
import cn.devzyh.oms.coms.flow.context.FlowContext;
import cn.devzyh.oms.coms.flow.service.EdgeService;
import cn.devzyh.oms.coms.flow.service.FlowService;
import cn.devzyh.oms.coms.flow.service.NodeService;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

/**
 * 工作流执行器
 *
 * @author devzyh
 * @date 2024-02-26
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FlowExecutor {

	private final FlowService flowService;

	private final EdgeService edgeService;

	private final NodeService nodeService;

	/**
	 * 最大执行节点数限制，避免过大影响效率
	 */
	private static final int MAX_NODE_LIMIT = 99;

	/**
	 * 起始节点ID
	 */
	public static final long START_NODE_ID = 1L;

	/**
	 * 终止节点ID
	 */
	private static final long END_NODE_ID = 99999L;


	/**
	 * 流程测试
	 *
	 * @param flowId 流程ID
	 */
	public R test(Long flowId) {
		R res = execute(flowId, null, true);
		flowService.changeStatus(flowId, res.success());
		return res;
	}

	/**
	 * 流程执行
	 *
	 * @param flowId 流程ID
	 * @param params 执行参数
	 * @return
	 */
	public R run(Long flowId, Map<String, Object> params) {
		Flow flow = flowService.getById(flowId);
		if (flow == null) {
			return R.failedMessage(FlowErrorCodes.FLOW_NOT_EXISTS, flowId.toString());
		}
		if (params == null || params.isEmpty()) {
			return R.failedMessage(FlowErrorCodes.FLOW_REQUIRED_PARAM_EMPTY, flowId.toString(), "");
		}

		String[] keys = flow.getRequiredParam().split(StrUtil.COMMA);
		for (String key : keys) {
			if (StrUtil.isBlank(params.get(key).toString())) {
				return R.failedMessage(FlowErrorCodes.FLOW_REQUIRED_PARAM_EMPTY, flowId.toString(), key);
			}
		}

		return execute(flowId, params, false);
	}

	/**
	 * 流程执行
	 *
	 * @param flowId 流程ID
	 * @param params 执行参数
	 * @param test   是否测试流程
	 * @return
	 */
	public R execute(Long flowId, Map<String, Object> params, boolean test) {
		FlowContext ctx = new FlowContext(flowId);
		ctx.setTest(test);
		ctx.setParams(params);

		R res = R.ok(); // 流程执行结果
		R<FlowNode> rNode = nextNode(ctx); // 节点获取结果

		int processedCount = 0;
		while (rNode.success()) {
			FlowNode fn = rNode.getData();
			if (fn == null) {
				break;
			}

			// 测试模式跳过执行
			if (!ctx.isTest()) {
				res = fn.process(ctx);
				if (res.failure()) {
					break;
				}

				// 记录成功执行的节点
				ctx.processed(fn);
			}

			// 死循环检查
			if (processedCount > MAX_NODE_LIMIT) {
				res = R.failedMessage(FlowErrorCodes.MAX_NODE_LIMIT, ctx.getFlowId().toString(), processedCount);
				break;
			}

			// 计算下一个节点
			rNode = nextNode(ctx);
			processedCount++;
		}

		// 非终点结束
		if (rNode.success() && res.success() && ctx.getCurrentNodeId() != END_NODE_ID) {
			res = R.failedMessage(FlowErrorCodes.FLOW_END_EXCEPTION, ctx.getFlowId().toString());
		}

		// 执行失败回滚
		if (rNode.failure() || res.failure()) {
			String errorMsg = rNode.failure() ? rNode.getMsg() : res.getMsg();
			if (ctx.rollback()) {
				// 回滚成功
				return R.failedMessage(FlowErrorCodes.ROLLBACK_SUCCESS, ctx.getFlowId().toString(), errorMsg);
			} else {
				// 回滚失败
				return R.failedMessage(FlowErrorCodes.ROLLBACK_FAILURE, ctx.getFlowId().toString(), errorMsg);
			}
		}

		return R.ok();
	}

	/**
	 * 下一个流程节点
	 *
	 * @param ctx
	 * @return
	 */
	R<FlowNode> nextNode(FlowContext ctx) {
		List<Edge> edges = edgeService.listEdges(ctx.getFlowId(), ctx.getCurrentNodeId());
		if (CollectionUtil.isEmpty(edges)) {
			return R.failedMessage(FlowErrorCodes.EDGE_NOT_EXISTS, ctx.getFlowId().toString(), ctx.getCurrentNodeId().toString());
		}

		Long endNodeId = null;
		for (Edge edge : edges) {
			if (StrUtil.isBlank(edge.getExpression())) {
				endNodeId = edge.getEndNodeId();
				break;
			}

			if (ctx.isTest()) {
				Map<String, Object> params;
				try {
					params = JSON.parseObject(edge.getTestValue());
				} catch (JSONException e) {
					return R.failedMessage(FlowErrorCodes.EDGE_TEST_VALUE_EXCEPTION, edge.getId().toString());
				}
				ctx.getOutput().putAll(params);
			}
			// 执行条件判断
			R res = checkSpringEL(edge, ctx.getOutput());
			if (res.success()) {
				endNodeId = edge.getEndNodeId();
				break;
			} else {
				// 出现错误信息代表表达式计算错误，否则时正常的校验失败
				if (StrUtil.isNotBlank(res.getMsg())) {
					return res;
				}
			}
		}

		Node node = null;
		if (endNodeId != null) {
			if (endNodeId == END_NODE_ID) {
				ctx.setCurrentNodeId(endNodeId);
				return R.ok();
			}

			node = nodeService.getById(endNodeId);
		}
		if (node == null) {
			return R.failedMessage(FlowErrorCodes.END_NODE_NOT_EXISTS, ctx.getFlowId().toString(), ctx.getCurrentNodeId().toString());
		}

		FlowNode fn = SpringUtil.getBean(node.getService());
		if (fn == null) {
			return R.failedMessage(FlowErrorCodes.SERVICE_NOT_EXISTS, ctx.getFlowId().toString(), ctx.getCurrentNodeId().toString());
		}

		ctx.setCurrentNodeId(endNodeId);
		return R.ok(fn);
	}

	/**
	 * 执行条件检查
	 *
	 * @param edge
	 * @param output
	 * @return
	 */
	R<String> checkSpringEL(Edge edge, Map<String, Object> output) {
		TemplateParserContext template = new TemplateParserContext();
		ExpressionParser parser = new SpelExpressionParser();
		EvaluationContext context = new StandardEvaluationContext();
		for (String key : output.keySet()) {
			context.setVariable(key, output.get(key));
		}

		try {
			Boolean result = parser.parseExpression(template.getExpressionPrefix() + edge.getExpression() + template.getExpressionSuffix(),
					template).getValue(context, Boolean.class);
			if (Boolean.TRUE.equals(result)) {
				return R.ok();
			} else {
				return R.failed();
			}
		} catch (EvaluationException e) {
			return R.failedMessage(FlowErrorCodes.EDGE_SPRING_EL_EXCEPTION, edge.getId().toString(), e.getMessage());
		}
	}

}


